[FFmpeg] AVPacket 的使用记录(初始化、引用、解引用、释放) 您所在的位置:网站首页 ffmpeg packet frame区别 [FFmpeg] AVPacket 的使用记录(初始化、引用、解引用、释放)

[FFmpeg] AVPacket 的使用记录(初始化、引用、解引用、释放)

2024-01-10 10:43| 来源: 网络整理| 查看: 265

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录 前言一、先看下与AVPacket相关的几个重要函数1.AVPacket *av_packet_alloc(void)2. int av_new_packet(AVPacket *pkt, int size)3. void av_packet_free(AVPacket **pkt)4.int av_packet_ref(AVPacket *dst, const AVPacket *src)5.void av_packet_unref(AVPacket *pkt)6.void av_init_packet(AVPacket *pkt) 二、解决问题1.av_packet_unref 是否能释放整个AVPacket2. 多线程中应该如何使用以上函数

前言

在多线程编程中使用ffmpeg时,大家都应该会关注AVPacket的内存问题,不同线程中如何使用同一个AVPacket的数据,保证线程安全,还要减小数据复制

一、先看下与AVPacket相关的几个重要函数 1.AVPacket *av_packet_alloc(void)

初始化一个AVPacket,这里malloc了一个AVPacket,同时初始化了一部分参数参数,av_packet_unref(pkt) --> av_init_packet(pkt);,使用结束后需使用av_packet_free 进行释放,也有人说调用av_packet_unref 进行释放,但看源码就可知道,av_packet_unref 只是解引用了buf的数据区,并不会释放整个AVPacket,第二章节会有一个小测试

AVPacket *av_packet_alloc(void) { AVPacket *pkt = av_mallocz(sizeof(AVPacket)); if (!pkt) return pkt; av_packet_unref(pkt); return pkt; } 2. int av_new_packet(AVPacket *pkt, int size)

new一个新的Packet 主要是创建一个新的AVBufferRef ,将pkt->data与buf进行绑定,这将为后面的引用提供条件

int av_new_packet(AVPacket *pkt, int size) { AVBufferRef *buf = NULL; int ret = packet_alloc(&buf, size); if (ret buf = buf; pkt->data = buf->data; pkt->size = size; return 0; } 3. void av_packet_free(AVPacket **pkt)

完全释放AVPacket,如果pkt存在引用,将会释放与所有引用,也就是说如果调用它,与pkt绑定的与的引用将失效,后续有测试代码说明

void av_packet_free(AVPacket **pkt) { if (!pkt || !*pkt) return; av_packet_unref(*pkt); av_freep(pkt); } 4.int av_packet_ref(AVPacket *dst, const AVPacket *src)

将dst与src进行引用绑定,线程安全,注意看源码,引用实际是引用的buf,如果没有buf,这里会初始化一个buf并将buf与data绑定 av_packet_copy_props 会拷贝内容参数

int av_packet_ref(AVPacket *dst, const AVPacket *src) { int ret; ret = av_packet_copy_props(dst, src); if (ret buf) { ret = packet_alloc(&dst->buf, src->size); if (ret size || src->data); if (src->size) memcpy(dst->buf->data, src->data, src->size); dst->data = dst->buf->data; } else { dst->buf = av_buffer_ref(src->buf); if (!dst->buf) { ret = AVERROR(ENOMEM); goto fail; } dst->data = src->data; } dst->size = src->size; return 0; fail: av_packet_free_side_data(dst); return ret; } 5.void av_packet_unref(AVPacket *pkt)

解引用,引用计数减1,没什么好说的

void av_packet_unref(AVPacket *pkt) { av_packet_free_side_data(pkt); av_buffer_unref(&pkt->buf); av_init_packet(pkt); pkt->data = NULL; pkt->size = 0; } 6.void av_init_packet(AVPacket *pkt)

初始化AVPacket里的部分参数,没什么好说的

void av_init_packet(AVPacket *pkt) { pkt->pts = AV_NOPTS_VALUE; pkt->dts = AV_NOPTS_VALUE; pkt->pos = -1; pkt->duration = 0; #if FF_API_CONVERGENCE_DURATION FF_DISABLE_DEPRECATION_WARNINGS pkt->convergence_duration = 0; FF_ENABLE_DEPRECATION_WARNINGS #endif pkt->flags = 0; pkt->stream_index = 0; pkt->buf = NULL; pkt->side_data = NULL; pkt->side_data_elems = 0; } 二、解决问题 1.av_packet_unref 是否能释放整个AVPacket

用一段小代码就可以测试出

while (1) { AVPacket* pkt = av_packet_alloc(); // std::this_thread::sleep_for(1ms); av_packet_unref(pkt); // av_packet_free(&pkt); }

如果只调用 av_packet_unref 而不调用 av_packet_free 你会发现内存会疯狂的上涨,而调用av_packet_free 内存会很平稳,注意 av_packet_alloc 每次只会占用sizeof(AVPacket)的大小,实际为88字节,如果加了sleep,内存增长速度会很慢 因此可以得出结论,调用 av_packet_alloc 必需使用 av_packet_free 进行内存释放. av_packet_unref 可以释放 av_new_packet 的内存,可使用以下代码测试

while (1) { AVPacket* pkt = av_packet_alloc(); av_new_packet(pkt, 1000000); std::this_thread::sleep_for(10ms); av_packet_unref(pkt);//可以注释此行来查看是否释放内存 // av_packet_free(&pkt); } 2. 多线程中应该如何使用以上函数

我们设想一个场景: 线程1:负责生产AVPacket,我们不一定是使用的ffmpeg解封装器,有可能是自己创建的,比如接收的视频数据是裸H264/H265的网络数据,或者是live555接收的数据,又或者是直接从文件读取的h264/h265数据 线程2:负责解码AVPacket,这个线程专门解码数据 线程3:负责录像,其实就是将AVPacket写入到文件 线程4:负责推流,需要将AVPacket写入到其它网络流中 这时候就得考虑一些问题,什么时候创建AVPacket,什么时候创建buf,什么时候引用,引用后又什么时候解引用

代码1:

AVPacket* pkt = av_packet_alloc(); av_new_packet(pkt, 1000); print(pkt, "pkt"); AVPacket* pkt1 = av_packet_alloc(); av_packet_ref(pkt1, pkt); print(pkt, "pkt"); print(pkt1, "pkt1"); AVPacket* pkt2 = av_packet_alloc(); av_packet_ref(pkt2, pkt); print(pkt, "pkt"); print(pkt2, "pkt2"); av_packet_unref(pkt); av_new_packet(pkt, 2000); AVPacket* pkt3 = av_packet_alloc(); av_packet_ref(pkt3, pkt); print(pkt, "pkt"); print(pkt1, "pkt1"); print(pkt2, "pkt2"); print(pkt3, "pkt3"); av_packet_free(&pkt1); av_packet_free(&pkt2); av_packet_free(&pkt3); print(pkt, "pkt"); print(pkt1, "pkt1"); print(pkt2, "pkt2"); print(pkt3, "pkt3"); av_packet_unref(pkt); print(pkt, "pkt"); void print(AVPacket* pkt, const char* objectname) { if (pkt) { if (pkt->data) { auto re = av_buffer_get_ref_count(pkt->buf); LOG_DEBUG LOG_DEBUG


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有